package com.huanke.iot.gateway.onlinecheck;

import com.huanke.iot.base.constant.DeviceConstant;
import com.huanke.iot.base.dao.device.DeviceMapper;
import com.huanke.iot.base.dao.device.data.DeviceOperLogMapper;
import com.huanke.iot.base.po.device.DevicePo;
import com.huanke.iot.base.po.device.data.DeviceOperLogPo;
import com.huanke.iot.gateway.powercheck.PowerCheckService;
import io.netty.util.concurrent.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Repository
@Slf4j
public class OnlineCheckService {

    @Value("${useNewGateway}")
    private String useNewGateway;

    @Value("${maxChildNumber}")
    private int maxChildNumber;

    private ConcurrentHashMap<Integer, OnlineCheckData> idMap =
            new ConcurrentHashMap<>(2048);

    private ConcurrentHashMap<String, OnlineCheckData> childIdMap =
            new ConcurrentHashMap<>(2048);

    @Autowired
    private DeviceMapper deviceMapper;

    private long l = System.currentTimeMillis();

    @Autowired
    private DeviceOperLogMapper deviceOperLogMapper;

    private ScheduledExecutorService executorService;

    @Autowired
    private PowerCheckService powerCheckService;

    @PostConstruct
    public void init(){
        loadOnlineFromDb();
        executorService = Executors.newScheduledThreadPool(1,new DefaultThreadFactory("ScanIdThread"));
        executorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    doScan();
                    doChildScan();
                }catch (Exception e){
                    log.error("",e);
                }
            }
        },0,60, TimeUnit.SECONDS);
    }

    public void doScan() {
        log.info("doScan");
        Iterator<Map.Entry<Integer, OnlineCheckData>> it = idMap.entrySet().iterator();
        long temp = System.currentTimeMillis();
        while (it.hasNext()){
            OnlineCheckData data = it.next().getValue();
            if (data.getLastUpdateTime() < l ) {
                data.setFailCount(data.getFailCount() + 1);
                if (data.getFailCount() > 3) {
                    if(Boolean.FALSE.equals(Boolean.valueOf(useNewGateway))){
                        data.setOnline(false);
                    }

                }
            }
            if(!data.isOnline()){
                DevicePo updatePo = new DevicePo();
                updatePo.setOnlineStatus(DeviceConstant.ONLINE_STATUS_NO);
                updatePo.setLastOfflineTime(System.currentTimeMillis());
                updatePo.setId(data.getId());
                deviceMapper.updateById(updatePo);
                //将离线记录写入日志
                DeviceOperLogPo deviceOperLogPo= new DeviceOperLogPo();
                deviceOperLogPo.setDeviceId(data.getId());
                deviceOperLogPo.setFuncId("410");
                deviceOperLogPo.setFuncValue("0");
                deviceOperLogPo.setCreateTime(System.currentTimeMillis());
                this.deviceOperLogMapper.insert(deviceOperLogPo);
                it.remove();
            }
        }
        l = temp;
    }

    public void doChildScan() {
        log.info("doChildScan");
        Iterator<Map.Entry<String, OnlineCheckData>> it = childIdMap.entrySet().iterator();
        long temp = System.currentTimeMillis();
        while (it.hasNext()){
            Map.Entry<String, OnlineCheckData> map = it.next();
            OnlineCheckData data = map.getValue();
            if (data.getLastUpdateTime() < l ) {
                data.setFailCount(data.getFailCount() + 1);
                if (data.getFailCount() > 3) {
                    if(Boolean.FALSE.equals(Boolean.valueOf(useNewGateway))){
                        data.setOnline(false);
                    }

                }
            }
            if(!data.isOnline()){
                //更新从设备状态
                String key = map.getKey();
                //log.info("key = {}",key);
                String id = key.substring(0, key.indexOf("_"));
                String child = key.substring(id.length()+1);
                //log.info("id = {}",id);
                //log.info("child = {}",child);
                DevicePo updatePo = new DevicePo();
                updatePo.setHostDeviceId(Integer.valueOf(id));
                updatePo.setChildId(child);
                updatePo.setOnlineStatus(DeviceConstant.ONLINE_STATUS_NO);
                updatePo.setLastOnlineTime(System.currentTimeMillis());
                deviceMapper.updateChildStatus(updatePo);
                it.remove();
            }
        }
        l = temp;
    }

    public void resetOffline(Integer id){
        //主设备离线
        DevicePo updatePo = new DevicePo();
        updatePo.setOnlineStatus(DeviceConstant.ONLINE_STATUS_NO);
        updatePo.setPowerStatus(DeviceConstant.POWER_STATUS_NO);
        updatePo.setLastOfflineTime(System.currentTimeMillis());
        updatePo.setId(id);
        deviceMapper.updateById(updatePo);
        //将离线记录写入日志
        DeviceOperLogPo deviceOperLogPo= new DeviceOperLogPo();
        deviceOperLogPo.setDeviceId(id);
        deviceOperLogPo.setFuncId("410");
        deviceOperLogPo.setFuncValue("0");
        deviceOperLogPo.setCreateTime(System.currentTimeMillis());
        this.deviceOperLogMapper.insert(deviceOperLogPo);
        //删除idMap中该设备
        idMap.remove(id);
        //从设备离线
        for (int i = 0;i < maxChildNumber;i++){
            String key = id+"_"+i;
            childIdMap.remove(key);
        }
    }

    public void resetOnline(Integer deviceId){
        OnlineCheckData data = idMap.get(deviceId);
        boolean needUpdateDd  = false;
        if(data == null){
            data = new OnlineCheckData();
            needUpdateDd = true;
        }else if(!data.isOnline()){
            needUpdateDd = true;
        }
        data.setFailCount(0);
        data.setLastUpdateTime(System.currentTimeMillis());
        data.setOnline(true);
        //上次记录为离线时才更新状态
        data.setId(deviceId);
        idMap.put(deviceId,data);
        if(needUpdateDd){
            DevicePo updatePo = new DevicePo();
            updatePo.setId(deviceId);
            updatePo.setOnlineStatus(DeviceConstant.ONLINE_STATUS_YES);
            updatePo.setPowerStatus(DeviceConstant.POWER_STATUS_YES);
            updatePo.setLastOnlineTime(System.currentTimeMillis());
            deviceMapper.updateById(updatePo);
            //记录离线后的首次上线时间
            DeviceOperLogPo deviceOperLogPo= new DeviceOperLogPo();
            deviceOperLogPo.setDeviceId(deviceId);
            deviceOperLogPo.setFuncId("410");
            deviceOperLogPo.setFuncValue("1");
            deviceOperLogPo.setCreateTime(System.currentTimeMillis());
            this.deviceOperLogMapper.insert(deviceOperLogPo);
        }
    }

    /**
     * 重设从设备在线状态
     * @param deviceId
     * @param childId
     */
    public void resetChildOnline(Integer deviceId,String childId){
        String key = deviceId+"_"+childId;
        OnlineCheckData data = childIdMap.get(key);
        //log.info("key:"+key);
        //log.info("data:"+data);
        boolean needUpdateDd  = false;
        if(data == null){
            needUpdateDd = true;
            data = new OnlineCheckData();
        }else if(!data.isOnline()){
            needUpdateDd = true;
        }
        data.setFailCount(0);
        data.setLastUpdateTime(System.currentTimeMillis());
        data.setOnline(true);
        //上次记录为离线时才更新状态
        data.setId(Integer.parseInt(childId));
        childIdMap.put(key,data);
        //log.info("resetChildOnline-needUpdateDd = {}",needUpdateDd);
        if(needUpdateDd){
            DevicePo updatePo = new DevicePo();
            updatePo.setHostDeviceId(deviceId);
            updatePo.setChildId(childId);
            updatePo.setOnlineStatus(DeviceConstant.ONLINE_STATUS_YES);
            updatePo.setLastOnlineTime(System.currentTimeMillis());
            deviceMapper.updateChildStatus(updatePo);
            if (data != null){
                //记录离线后的首次上线时间
                DeviceOperLogPo deviceOperLogPo= new DeviceOperLogPo();
                deviceOperLogPo.setDeviceId(data.getId());
                deviceOperLogPo.setFuncId("410");
                deviceOperLogPo.setFuncValue("1");
                deviceOperLogPo.setCreateTime(System.currentTimeMillis());
                deviceOperLogMapper.insert(deviceOperLogPo);
            }
        }
    }

    private void loadOnlineFromDb(){
        System.out.println("loadOnlineFromDb");
        DevicePo queryPo = new DevicePo();
        queryPo.setOnlineStatus(DeviceConstant.ONLINE_STATUS_YES);
        queryPo.setStatus(1);
        List<DevicePo> devicePoList = deviceMapper.queryList(queryPo,100000,0);
        devicePoList.forEach(devicePo -> {
            OnlineCheckData data = new OnlineCheckData();
            data.setFailCount(0);
            data.setLastUpdateTime(System.currentTimeMillis());
            data.setOnline(true);
            data.setId(devicePo.getId());
            String childId = devicePo.getChildId();
            if (StringUtils.isEmpty(childId)){
                idMap.put(devicePo.getId(),data);
                if (devicePo.getPowerStatus() == DeviceConstant.POWER_STATUS_NO){
                    DevicePo updatePo = new DevicePo();
                    updatePo.setId(devicePo.getId());
                    updatePo.setPowerStatus(DeviceConstant.POWER_STATUS_YES);
                    deviceMapper.updateById(updatePo);
                }
            }else{
                String key = devicePo.getHostDeviceId()+"_"+childId;
                log.info("key = {}",key);
                data.setId(Integer.valueOf(devicePo.getChildId()));
                childIdMap.put(key,data);
            }
        });
    }
}
